﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;

namespace TimeClock
{
	public partial class TimeClock : Form, IDisposable
	{
		public TimeClock()
		{
			InitializeComponent();

			TimeWorking = new TimeSpan();
			_timeHistory = new DataSet();
			_manualTimeHistory = new DataSet();
			_blacklist = new DataSet();
			_stateManager = StateManager.Get();
			_sql = new SqlHelper();

			components.Add( _timeHistory );
			components.Add( _manualTimeHistory );

			UpdateHistoryTab();
			UpdateBlacklistTab();
		}

		private void UpdateBlacklistTab()
		{
			_blacklist.Clear();

			using ( var connection = _sql.GetConnection() )
			{
				using ( var command = _sql.GetCommand( "GetBlacklist", connection ) )
				{
					IDataAdapter adapter = new SqlDataAdapter( command );
					adapter.Fill( _blacklist );
				}
			}

			
			//dgBlacklist.DataSource = _blacklist.Tables[ 0 ];
			dgBlacklist.DataBindings.Add( new Binding( "ID", _blacklist.Tables[ 0 ], "BlacklistID" ) );
			dgBlacklist.DataBindings.Add( new Binding( "Type", _blacklist.Tables[ 0 ], "Type" ) );
			dgBlacklist.DataBindings.Add( new Binding( "Name", _blacklist.Tables[ 0 ], "Name" ) );
			dgBlacklist.AutoResizeColumns( DataGridViewAutoSizeColumnsMode.ColumnHeader );
		}


		public bool Working { get; set; }
		public TimeSpan TimeWorking { get; set; }
		public DateTime TimeStarted { get; set; }

		private void UpdateHistoryTab()
		{
			_timeHistory.Clear();
			_manualTimeHistory.Clear();

			using ( var connection = _sql.GetConnection() )
			{
				using ( var command = _sql.GetCommand( "GetHistory", connection ) )
				{
					IDataAdapter adapter = new SqlDataAdapter( command );
					adapter.Fill( _timeHistory );
				}

				using ( SqlCommand command = _sql.GetCommand( "GetManualEntries", connection ) )
				{
					IDataAdapter adapter = new SqlDataAdapter( command );
					adapter.Fill( _manualTimeHistory );
				}
			}

			dgHistory.DataSource = _timeHistory.Tables[ 0 ];
			dgHistory.AutoResizeColumns( DataGridViewAutoSizeColumnsMode.ColumnHeader );

			dgManualEntries.DataSource = _manualTimeHistory.Tables[ 0 ];
			dgManualEntries.AutoResizeColumns( DataGridViewAutoSizeColumnsMode.ColumnHeader );
			dgManualEntries.Columns[ 0 ].ReadOnly = true;
		}

		private void TimeClock_FormClosing( object sender, FormClosingEventArgs e )
		{
			if ( Working )
			{
				btnStop_Click( null, null );
			}
		}

		private void TimeClock_Load( object sender, EventArgs e )
		{
			lblTheDate.Text = DateTime.Now.ToShortDateString();

		}

		private void btnStart_Click( object sender, EventArgs e )
		{
			TimeStarted = DateTime.Now;
			timerTimeWorked.Start();
			_stateManager.ChangeState( ApplicationState.Running );
		}

		private void btnStop_Click( object sender, EventArgs e )
		{
			_stateManager.ChangeState( ApplicationState.Paused );
			timerTimeWorked.Stop();
			UpdateDatabaseHistory( TimeStarted, DateTime.Now );
			UpdateHistoryTab();
		}

		private void UpdateDatabaseHistory( DateTime TimeStarted, DateTime dateTime )
		{
			using ( var connection = _sql.GetConnection() )
			{
				using ( var command = _sql.GetCommand( "AddHistory", connection ) )
				{
					command.Parameters.Add( new SqlParameter( "@StartTime", TimeStarted ) );
					command.Parameters.Add( new SqlParameter( "@EndTime", TimeStarted + TimeWorking ) );

					command.ExecuteNonQuery();
				}
			}
		}

		private void btnSubtract_Click( object sender, EventArgs e )
		{
			if ( TimeWorking.Minutes >= 10 )
			{
				TimeStarted += new TimeSpan( 0, 10, 0 );
				timerTimeWorked_Tick( null, null );
			}
		}

		private void timerTimeWorked_Tick( object sender, EventArgs e )
		{
			TimeWorking = DateTime.Now - TimeStarted;
			lblTimeWorked.Text = String.Format( "{0}:{1}:{2}", TimeWorking.Hours.ToString(), TimeWorking.Minutes.ToString(), TimeWorking.Seconds.ToString() );
		}

		private DataSet _timeHistory;
		private DataSet _manualTimeHistory;
		private StateManager _stateManager;
		private SqlHelper _sql;
		private DataSet _blacklist;

		private void dgManualEntries_RowsRemoved( object sender, DataGridViewRowsRemovedEventArgs e )
		{
			DataGridView dg = (DataGridView)sender;
			if ( e.RowIndex == dg.Rows.Count ) return;

			using ( var connection = _sql.GetConnection() )
			{
				using ( var command = _sql.GetCommand( "DeleteManualEntry", connection ) )
				{
					command.Parameters.Add( new SqlParameter( "@RowID", dg.Rows[ e.RowIndex ].Cells[ "ID" ].Value ) );
					command.ExecuteNonQuery();
				}

				dgManualEntries.Refresh();
			}
		}

		private void dgManualEntries_RowLeave( object sender, DataGridViewCellEventArgs e )
		{
			//HACK: for some reason this gets fired when you switch to the history tab and updates the first
			//row to itself.

			DataGridViewCellCollection cells = ( (DataGridView)sender ).Rows[ e.RowIndex ].Cells;

			if ( cells[ "ID" ].Value == DBNull.Value )
			{
				AddManualEntry( cells );
				UpdateHistoryTab();
			}
			else
			{
				UpdateManualEntry( cells );
				UpdateHistoryTab();
			}
		}

		private void UpdateManualEntry( DataGridViewCellCollection columns )
		{
			using ( var connection = _sql.GetConnection() )
			{
				using ( var command = _sql.GetCommand( "UpdateManualEntry", connection ) )
				{
					command.Parameters.Add( new SqlParameter( "@ID", columns[ "ID" ].Value ) );
					command.Parameters.Add( new SqlParameter( "@Date", columns[ "Date" ].Value ) );
					command.Parameters.Add( new SqlParameter( "@Hours", columns[ "HoursWorked" ].Value ) );
					command.Parameters.Add( new SqlParameter( "@Reason", columns[ "ReasonForManualEntry" ].Value.ToString() ) );

					command.ExecuteNonQuery();
				}
			}
		}

		private void AddManualEntry( DataGridViewCellCollection columns )
		{
			using ( var connection = _sql.GetConnection() )
			{
				using ( var command = _sql.GetCommand( "AddManualEntry", connection ) )
				{
					command.Parameters.Add( new SqlParameter( "@Date", columns[ "Date" ].Value ) );
					command.Parameters.Add( new SqlParameter( "@Hours", columns[ "HoursWorked" ].Value ) );
					command.Parameters.Add( new SqlParameter( "@Reason", columns[ "ReasonForManualEntry" ].Value.ToString() ) );

					command.ExecuteNonQuery();
				}
			}
		}

		private void dgManualEntries_CancelRowEdit( object sender, QuestionEventArgs e )
		{
			dgManualEntries.Refresh();
		}

		private void dgBlacklist_CellContentClick( object sender, DataGridViewCellEventArgs e )
		{

		}
	}

	public class StateManager
	{
		public static StateManager Get()
		{
			return new StateManager();
		}

		private StateManager()
		{
			if ( _state == null )
			{
				_state = ApplicationState.Off;
			}
		}

		public delegate void StateChanged( object sender, StateChangeEventArgs e );
		public event StateChanged OnStateChanged;

		public void ChangeState( ApplicationState proposedState )
		{
			if ( proposedState == ApplicationState.Paused && _state == ApplicationState.Running )
			{
				_state = proposedState;
			}
			else if ( proposedState == ApplicationState.Running && ( _state == ApplicationState.Paused || _state == ApplicationState.Off ) )
			{
				_state = proposedState;
			}
			else
			{
				throw new InvalidOperationException( String.Format( "Cannot switch from state {0} to {1}", _state.ToString(), proposedState.ToString() ) );
			}

			OnStateChanged( this, new StateChangeEventArgs( proposedState ) );
		}

		private static ApplicationState? _state = null;

		public ApplicationState? State
		{
			get { return _state; }
		}
	}

	public enum ApplicationState { Off, Paused, Running };
	public enum BlacklistTypes { Process, Website };

	public class StateChangeEventArgs : EventArgs
	{
		public StateChangeEventArgs( ApplicationState newState )
		{
			State = newState;
		}

		public ApplicationState State { get; set; }
	}

	public class ApplicationManager
	{
		public ApplicationManager()
		{
			_sql = new SqlHelper();
			_stateManager = StateManager.Get();
			_stateManager.OnStateChanged += new StateManager.StateChanged( _state_OnStateChanged );
		}

		void _state_OnStateChanged( object sender, StateChangeEventArgs e )
		{
			UpdateDbState( e.State );
		}

		private void UpdateDbState( ApplicationState applicationState )
		{
			if ( _stateManager.State == null ) return;

			using ( var connection = _sql.GetConnection() )
			{
				using ( var command = _sql.GetCommand( "ChangeState", connection ) )
				{
					command.Parameters.Add( new SqlParameter( "@State", _stateManager.State.ToString() ) );
				}
			}
		}

		private StateManager _stateManager;
		private SqlHelper _sql;
	}

	public class SqlHelper
	{
		public SqlConnection GetConnection()
		{
			SqlConnection conn = new SqlConnection( CONNECTION_STRING );
			conn.Open();

			return conn;
		}

		public SqlCommand GetCommand( string storedProcedureName, SqlConnection connection )
		{
			SqlCommand command = new SqlCommand( storedProcedureName, connection );
			command.CommandType = CommandType.StoredProcedure;

			return command;
		}

		private const string CONNECTION_STRING = "Data Source=localhost; Initial Catalog=PersonalProductivity;Trusted_Connection=Yes;";
	}

}
